package com.appspot.yttv.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Widget;

/**
 * 
 * Chromeless Player API
 * 
 * @author Uwe Maurer
 *
 */
public class ChromelessPlayer extends Widget {
	
	public enum PlayerState {
		STOPPED,
		PLAYING,
		PAUSED,
		LOADING,
		CUED,
	}
	
	public interface PlayerListener {
		public void onReady();
		public void onStateChange(PlayerState state);
		public void onError(int error);
	}
	
	public static class YTPlayer extends JavaScriptObject {
		protected YTPlayer() {
		}

		public native final void loadVideoById(String videoId, int startSeconds) /*-{
				this.loadVideoById(videoId, startSeconds);
		}-*/;
		
		public final void setPixelSize(int width, int height) {
			// swfobject creates an object tag with width and height attributes.
			// If we want to resize it change these (GWT Element.setSize() only
			// seems to alter the CSS width and height).
			((Element) this.cast()).setAttribute("width", ""+width);
			((Element) this.cast()).setAttribute("height", ""+height);
		}
		
		public native final void setVolume(int volume) /*-{
			this.setVolume(volume);
		}-*/;
		
		public native final int getVideoBytesLoaded() /*-{
			return this.getVideoBytesLoaded();
		}-*/;

		public native final int getVideoBytesTotal() /*-{
			return this.getVideoBytesTotal();
		}-*/;
		
		public native final int getPlayerState() /*-{
			return this.getPlayerState();
		}-*/;

		public native final double getDuration() /*-{
			return this.getDuration();
		}-*/;
	
		public native final double getCurrentTime() /*-{
			return this.getCurrentTime();
		}-*/;
		
		public native final void setFlashVariable(String name, String value) /*-{
			this.SetVariable(name, value);
		}-*/;

		public native final String getFlashVariable(String name) /*-{
			return this.GetVariable(name);
		}-*/;
	}

	private static final Map<String, ChromelessPlayer> map = new HashMap<String, ChromelessPlayer>();
	
	static {
		register();
	}

	private static native void register() /*-{ 
	  $wnd.onYouTubePlayerReady = function(playerId) {
	        var ytplayer = $wnd.document.getElementById(playerId + "api");
	      	@com.appspot.yttv.client.ChromelessPlayer::onReady(Ljava/lang/String;Lcom/appspot/yttv/client/ChromelessPlayer$YTPlayer;)(playerId, ytplayer);
	      	var name = 'onPlayerStateChange' +playerId;
	        $wnd[name]  = function(state) {
	        	@com.appspot.yttv.client.ChromelessPlayer::onStateChange(Ljava/lang/String;I)(playerId, state);
	        }
	        var errorname = 'onPlayerError' +playerId;
	        $wnd[errorname]  = function(error) {
	        	@com.appspot.yttv.client.ChromelessPlayer::onError(Ljava/lang/String;I)(playerId, error);
	        }
	      	ytplayer.addEventListener("onStateChange", name);
	      	ytplayer.addEventListener("onError", errorname);
	    }
	}-*/;

	@SuppressWarnings("unused")
	private static void onReady(String playerId, YTPlayer playerElement) {
		ChromelessPlayer player = map.get(playerId);
		if (player != null) {
			player.player = playerElement;
			player.fireOnReady();
		}
	}
	
	@SuppressWarnings("unused")
	private static void onStateChange(String playerId, int state) {
		ChromelessPlayer player = map.get(playerId);
		if (player != null) {
			player.fireOnStateChange(state);
		}
	}
	
	@SuppressWarnings("unused")
	private static void onError(String playerId, int error) {
		ChromelessPlayer player = map.get(playerId);
		if (player != null) {
			player.fireOnError(error);
		}
	}
	private final String name;
	private final int width;
	private final int height;

	private YTPlayer player;
	private List<PlayerListener> playerListeners;

	public ChromelessPlayer(String name, int width, int height) {
		Element elem = DOM.createDiv();
		elem.setId(name);
		setElement(elem);
		this.name = name;
		this.width = width;
		this.height = height;
		this.playerListeners = new ArrayList<PlayerListener>();
		map.put(name, this);
	}

	public void addPlayerListener(PlayerListener l) {
		this.playerListeners.add(l);
	}
	
	public void removePlayerListener(PlayerListener l) {
		this.playerListeners.remove(l);
	}
	
	private void fireOnStateChange(int s) {
		for (PlayerListener l : this.playerListeners) {
			l.onStateChange(mapPlayerState(s));
		}
	}

	private void fireOnError(int e) {
		for (PlayerListener l : this.playerListeners) {
			l.onError(e);
		}
	}

	private void fireOnReady() {
		for (PlayerListener l : this.playerListeners) {
			l.onReady();
		}
	}

	public void createSwf(int width, int height) {
		createSwf(name, width, height);
	}

	private native void createSwf(String name, int width, int height) /*-{ 
	     var params = { allowScriptAccess: "always", bgcolor: "#000000", wmode: "opaque" };
	     var atts = { id: name + "api" };
	     var swfArgs = { setVideoQuality: "2" }
	     $wnd.swfobject.embedSWF("http://www.youtube.com/apiplayer?enablejsapi=1&playerapiid=" + name,
	        name, ""+width, ""+height, "8", null, swfArgs, params, atts);

	}-*/;

	public void loadVideoById(String videoId, int startSeconds) {
		if (player != null) {
			player.loadVideoById(videoId, startSeconds);
		}
	}
	
	public void setPixelSize(int width, int height) {
		if (player != null) {
			player.setPixelSize(width, height);
		}
	}
	
	public void setVolume(int volume) {
		if (player != null) {
			player.setVolume(volume);
		}
	}
	
	public Element getPlayer() {
		return player.cast();
	}
	
	@Override
	protected void onLoad() {
		createSwf(width, height);
	}

	public PlayerState getState() {
		return (player != null) ? mapPlayerState(player.getPlayerState()) : PlayerState.STOPPED;
	}
	
	private PlayerState mapPlayerState(int s) {
		if (s >=0 && s < PlayerState.values().length) {
			PlayerState state = PlayerState.values()[s];
			return state;
		}
		return PlayerState.STOPPED;
	}

	public final int getVideoBytesLoaded() {
		return (player != null) ? player.getVideoBytesLoaded() : 0;
	}

	public final int getVideoBytesTotal() {
		return (player != null) ? player.getVideoBytesTotal() : 0;
	}

	public final double getDuration() {
		return (player != null) ? player.getDuration() : 0;
	}

	public double getCurrentTime() {
		if (player != null) {
			return player.getCurrentTime();
		} else {
			return 0;
		}
	}

	public final String getFlashVariable(String name) {
		return player.getFlashVariable(name);
	}

	public final void setFlashVariable(String name, String value) {
		player.setFlashVariable(name, value);
	}
}